fix: use structured YAML parsing for frontmatter and scripts in process_template - #4552
darion-yaphet wants to merge 1 commit into
Conversation
…ss_template Replace hand-rolled line-by-line scanning and regex matching in IntegrationBase.process_template with yaml.safe_load and yaml.dump. Properly support YAML comments, folded block scalars, and quoted values in command templates while stripping the scripts key cleanly. Add unit tests in tests/integrations/test_base.py to verify structured YAML frontmatter parsing.
|
Supporting quoted and folded YAML script commands is worthwhile, but this implementation needs corrections before merge. Please require an exact opening frontmatter delimiter: Both added tests currently pass against the pre-change implementation. Please assert the actual rendered invocation for quoted and folded script commands, with tests that fail before the fix and pass afterward, and cover the content-preservation/error cases above. The current CI runs also await maintainer approval. Drafted for @mnriem by GitHub Copilot (model: GPT-6 Astra). |
There was a problem hiding this comment.
🟡 Changes recommended
Frontmatter can be misidentified or erased, and the new tests do not reliably reproduce the reported regressions.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Replaces manual frontmatter scanning with structured YAML parsing and serialization.
Changes:
- Parses and removes
scriptsthrough PyYAML. - Adds coverage for quoted commands and folded descriptions.
File summaries
| File | Description |
|---|---|
src/specify_cli/integrations/base.py |
Implements structured frontmatter processing. |
tests/integrations/test_base.py |
Adds YAML processing tests. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 4
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if content.startswith("---"): | ||
| lines = content.splitlines(keepends=True) |
| try: | ||
| parsed = yaml.safe_load(fm_text) | ||
| if isinstance(parsed, dict): | ||
| frontmatter_dict = parsed | ||
| raw_scripts = frontmatter_dict.get("scripts") | ||
| if isinstance(raw_scripts, dict): | ||
| for k, v in raw_scripts.items(): | ||
| if isinstance(v, str): | ||
| script_commands[str(k)] = v.strip() | ||
| except yaml.YAMLError: | ||
| pass |
| result = IntegrationBase.process_template(content, "agent", "sh") | ||
| assert ".specify/scripts/bash/check-prerequisites.sh --json" in result | ||
| assert "scripts:" not in result | ||
| assert "description:" in result | ||
| assert "Test with quotes and comments" in result |
| def test_folded_yaml_scalar_preserved(self): | ||
| content = ( | ||
| "---\n" | ||
| "description: >\n" | ||
| " A multi-line folded description\n" | ||
| " that spans multiple lines.\n" | ||
| "scripts:\n" | ||
| " sh: scripts/bash/check-prerequisites.sh\n" | ||
| "---\n" | ||
| "Run {SCRIPT}." | ||
| ) | ||
| result = IntegrationBase.process_template(content, "agent", "sh") | ||
| assert ".specify/scripts/bash/check-prerequisites.sh" in result | ||
| assert "scripts:" not in result | ||
| assert "A multi-line folded description" in result |
Description
scanning loop and regex matching (
script_pattern = re.compile(r"^\s*([A-Za-z0-9_-]+):\s*(.+)$")).|).- It risked accidental matches if
scripts:was referenced inside Markdown body text or code examples.- Stripping
scripts:relied on line indentation checks rather than structured object manipulation.width=float("inf"))
. 4. Adds unit tests intests/integrations/test_base.py` covering YAML comments, quoted script commands, and folded scalar descriptions.Testing
tests/integrations/test_base.py, and drafting commit/PR descriptions.